////////////////////////////////////////////////////////////
// Headers
////////////////////////////////////////////////////////////
#include "EntityManager.hpp"

#include <SmegTools/Debug/ProfilerManager.hpp>

#include "MovableEntity.hpp"
#include "../Math/BoundingVolumes/BoundingBox.hpp"
#include "../Math/QuadTree/QuadTree.hpp"

#include <SmegTools/Debug/DebugNew.hpp>

namespace smeg {
    namespace core {
        
////////////////////////////////////////////////////////////
/// Constructor
////////////////////////////////////////////////////////////
EntityManager::EntityManager() :
    m_pRenderWindow( NULL ),
    m_pMovableEntitiesQuadTree( NULL ),
    m_pStaticEntitiesQuadTree( NULL )
{
}
    
////////////////////////////////////////////////////////////
/// Destructor
////////////////////////////////////////////////////////////
EntityManager::~EntityManager()
{
    DeInit();
}

////////////////////////////////////////////////////////////
/// Initialize the manager
////////////////////////////////////////////////////////////
void EntityManager::Init( sf::RenderWindow* _pRenderWindow ) {
    // Setup the render window
    m_pRenderWindow = _pRenderWindow;
}

////////////////////////////////////////////////////////////
/// DeInit the manager
////////////////////////////////////////////////////////////
void EntityManager::DeInit() {
    ClearScene();
    
    delete m_pStaticEntitiesQuadTree;
    m_pStaticEntitiesQuadTree = NULL;

    delete m_pMovableEntitiesQuadTree;
    m_pMovableEntitiesQuadTree = NULL;
}

////////////////////////////////////////////////////////////
/// Setup QuadTree partionning
///
/// \param _Region : Region to have the quad tree
///
////////////////////////////////////////////////////////////
void EntityManager::SetupQuadTree( const math::BoundingBox& _Region ) {
    if ( !m_pStaticEntitiesQuadTree ) {
        m_pStaticEntitiesQuadTree = new math::QuadTree();
    }

    m_pStaticEntitiesQuadTree->Create( _Region );

    if ( !m_pMovableEntitiesQuadTree ) {
        m_pMovableEntitiesQuadTree = new math::QuadTree();
    }

    m_pMovableEntitiesQuadTree->Create( _Region );
}

////////////////////////////////////////////////////////////
/// Clear the scene by removing all entities
////////////////////////////////////////////////////////////
void EntityManager::ClearScene() {
    for (td_BaseEntitySetIt it = m_Entities.begin(); it != m_Entities.end(); ++it) {
        delete (*it);
    }
    m_Entities.clear();
}

////////////////////////////////////////////////////////////
/// Render the entities
////////////////////////////////////////////////////////////
void EntityManager::UpdateEntities( float _fDeltaTime ) {
    PROFILER_START( "UpdateEntities" )
    for (td_BaseEntitySetIt it = m_Entities.begin(); it != m_Entities.end(); ++it) {
        (*it)->Update( _fDeltaTime );
    }
    PROFILER_STOP( "UpdateEntities" )
}

////////////////////////////////////////////////////////////
/// Render the entities
////////////////////////////////////////////////////////////
void EntityManager::RenderEntities() {
    PROFILER_START( "RenderEntities" )
    for (td_BaseEntitySetConstIt it = m_Entities.begin(); it != m_Entities.end(); ++it) {
        m_pRenderWindow->draw( (**it) );
    }
    PROFILER_STOP( "RenderEntities" )
}

////////////////////////////////////////////////////////////
/// Add a entity, if the entity is already in the system, don't add it
////////////////////////////////////////////////////////////
bool EntityManager::AddEntity( BaseEntity* _pEntity ) {
    if ( m_Entities.insert( _pEntity ).second ) {
        if ( m_pStaticEntitiesQuadTree ) {
            m_pStaticEntitiesQuadTree->Add( _pEntity );
        }

        return true;
    } else {
        return false;
    }
}

bool EntityManager::AddEntity( MovableEntity* _pEntity ) {
    if ( m_Entities.insert( _pEntity ).second ) {
        if ( m_pMovableEntitiesQuadTree ) {
            m_pMovableEntitiesQuadTree->Add( _pEntity );
        }

        return true;
    } else {
        return false;
    }
}

////////////////////////////////////////////////////////////
/// Remove a entity
////////////////////////////////////////////////////////////
void EntityManager::RemoveEntity( BaseEntity* _pEntity, bool _bDeleteEntity ) {
    td_BaseEntitySetIt it = m_Entities.find( _pEntity );

    if ( it != m_Entities.end() ) {
        m_Entities.erase( it );
    }

    _pEntity->RemoveFromQuadTree();
    if ( _bDeleteEntity ) {
        delete _pEntity;
    }
}
    
////////////////////////////////////////////////////////////
/// Query a region for entities
////////////////////////////////////////////////////////////
void EntityManager::QueryMovableEntitiesRegion( const math::BoundingBox& _Region, std::vector<BaseObject*>& _Result, math::BoundingVolume::eBoundingVolume _eType ) const {
    if ( m_pMovableEntitiesQuadTree ) {
        m_pMovableEntitiesQuadTree->QueryRegion( _Region, _Result, _eType );
    } else {
        BaseEntity* pEntity;
        for (td_BaseEntitySetIt it = m_Entities.begin(); it != m_Entities.end(); ++it) {
            pEntity = *it;

            if ( pEntity->HasBoundingVolume( _eType ) &&
                dynamic_cast<MovableEntity*>( pEntity ) != NULL && 
                _Region.Contains( pEntity->getPosition() ) ) {
                _Result.push_back( pEntity );
            }
        }
    }
}

void EntityManager::QueryStaticEntitiesRegion( const math::BoundingBox& _Region, std::vector<BaseObject*>& _Result, math::BoundingVolume::eBoundingVolume _eType ) const {
    if ( m_pStaticEntitiesQuadTree ) {
        m_pStaticEntitiesQuadTree->QueryRegion( _Region, _Result, _eType );
    } else {
        BaseEntity* pEntity;
        for (td_BaseEntitySetIt it = m_Entities.begin(); it != m_Entities.end(); ++it) {
            pEntity = *it;

            if ( pEntity->HasBoundingVolume( _eType ) &&
                dynamic_cast<MovableEntity*>( pEntity ) == NULL && 
                _Region.Contains( pEntity->getPosition() ) ) {
                _Result.push_back( pEntity );
            }
        }
    }
}

#ifdef _DEBUG
void EntityManager::DebugRender( const sf::Color& _Color ) {
    if ( m_pStaticEntitiesQuadTree ) {
        m_pStaticEntitiesQuadTree->DebugRender( m_pRenderWindow );
    }
    if ( m_pMovableEntitiesQuadTree ) {
        m_pMovableEntitiesQuadTree->DebugRender( m_pRenderWindow );
    }

    for (td_BaseEntitySetIt it = m_Entities.begin(); it != m_Entities.end(); ++it) {
        (*it)->DebugRender( m_pRenderWindow, _Color );
    }
}
#endif //_DEBUG

    } // namespace core
} // namespace smeg
